/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */

#include "DiagramScene.h"

#include <QtGui/QtGui>
#include "DiagramView.h"
#include "DiagramEdge.h"
#include "TransactionDatabase.h"
#include "TransactionInfoItem.h"
#include "StockDatabase.h"

namespace QSThink
{

	DiagramScene::DiagramScene(DiagramView *view)
		: QGraphicsScene(view),_view(view),_infoBox(0),_type(Graph_Type_Unknown),
	_timelineItem(0),_timeIndicator(0),_priceIndicator(0),
	_dayHighestBalancePrice(0),_dayLowestBalancePrice(0),
	_dayHighestHighPrice(0),_dayLowestHighPrice(0),
	_dayHighestLowPrice(0),_dayLowestLowPrice(0),
	_dayHighestOpenPrice(0),_dayLowestOpenPrice(0),
	_dayHighestClosePrice(0),_dayLowestClosePrice(0)
	{
		

	}

	void DiagramScene::loadDayPrice( QString symbol )
	{
		QDate startDate=TransactionDatabase::getSingleton().getStartDate(symbol);
		QDate endDate=TransactionDatabase::getSingleton().getEndDate(symbol);
		

		loadDayPrice(symbol,startDate,endDate);
	}


	void DiagramScene::loadDayCapital( QString symbol )
	{
		QDate startDate=TransactionDatabase::getSingleton().getStartDate(symbol);
		QDate endDate=TransactionDatabase::getSingleton().getEndDate(symbol);


		loadDayCapital(symbol,startDate,endDate);

	}

	
	void DiagramScene::loadDayPrice( QString symbol,QDate date)
	{
		_nodes.clear();
		this->clear();
		setSceneRect(0,0,MAX_WIDTH,MAX_HEIGHT);

		trans=TransactionDatabase::getSingleton().getDayTransaction(symbol,date);

		if(trans->loadData()){
			
			int highPrice=trans->highPrice();
			int lowPrice=trans->lowPrice();
			int balancePrice=trans->balancePrice();

			int verticalInterval=highPrice-lowPrice;


			int cnt=trans->get2MinutesCount();
			for(int i=0;i<cnt;++i){
				Transaction2Minutes* t=trans->get2MinutesTransaction(i);
				float y=get2MinutesYPos(t);
				float x=get2MinutesXPos(t);
				assert(y>0);
				if(x<0){//ignore <9:30's transactions
					continue;
				}


				DiagramNode *node=new DiagramNode(_view,trans);
				this->addItem(node);
				
				int cnt=_nodes.size();
				if(cnt>0){//have prev node
					DiagramEdge* e=new DiagramEdge(node, _nodes[cnt-1]);
					this->addItem(e);
					_edges.push_back(e);
				}

				node->setPos(x,y);
				_nodes.push_back(node);

			}

		}


	}

	void DiagramScene::loadDayCapital( QString symbol,QDate startDate,QDate endDate )
	{
		_type=Graph_Type_Capital;
		clearNodes();

		calcCapital1Day(symbol, startDate, endDate);


		addTopTransInfoItem();
		addTimeIndicatorItem();
		addBottomTimeLine();
		addPriceIndicator();

		int cnt=_transactions.size();
		for(int i=0;i<cnt;++i){
			float x=float(i)/((float)cnt-1.0f)*(float)MAX_WIDTH;


			double total=_highestCapital.capital()-_lowestCapital.capital();

			double capital=StockDatabase::getSingleton().getStockCapital(_transactions[i]->symbol(),_transactions[i]->day());
			double offset=capital-_lowestCapital.capital();

			float y=MAX_HEIGHT-offset/total*(float)(MAX_HEIGHT-Top_Height-Bottom_Height)-Bottom_Height;//downward y//20 is bottom

			DiagramNode *node=new DiagramNode(_view,_transactions[i]);
			this->addItem(node);


			Transaction* t=_transactions[i];
			addBottomTimeLineTrans(x,t);

			int cnt=_nodes.size();
			if(cnt>0){//have prev node
				DiagramEdge* e=new DiagramEdge(node, _nodes[cnt-1]);
				this->addItem(e);
				_edges.push_back(e);

			}

			node->setPos(x,y);
			_nodes.push_back(node);


		}



	}


	void DiagramScene::loadDayPrice( QString symbol,QDate startDate,QDate endDate )
	{
		_type=Graph_Type_Price;
		clearNodes();


		
		calcTransaction1Day(symbol, startDate, endDate);


		addTopTransInfoItem();
		addTimeIndicatorItem();
		addBottomTimeLine();
		addPriceIndicator();

		int cnt=_transactions.size();
		for(int i=0;i<cnt;++i){
			float x=float(i)/((float)cnt-1.0f)*(float)MAX_WIDTH;


			float total=_dayHighestHighPrice->highPrice()-_dayLowestLowPrice->lowPrice();
			float offset=_transactions[i]->balancePrice()-_dayLowestLowPrice->lowPrice();

			float y=MAX_HEIGHT-offset/total*(float)(MAX_HEIGHT-Top_Height-Bottom_Height)-Bottom_Height;//downward y//20 is bottom

			DiagramNode *node=new DiagramNode(_view,_transactions[i]);
			this->addItem(node);


			Transaction* t=_transactions[i];
			addBottomTimeLineTrans(x,t);

			int cnt=_nodes.size();
			if(cnt>0){//have prev node
				DiagramEdge* e=new DiagramEdge(node, _nodes[cnt-1]);
				this->addItem(e);
				_edges.push_back(e);

			}

			node->setPos(x,y);
			_nodes.push_back(node);


		}


		
	}

	float DiagramScene::getYPos( Transaction1Day* trans, int index )
	{
		TransactionSingle* t=trans->getTransaction(index);
		float vertInterval=trans->highPrice()-trans->lowPrice();
		float offset=t->price-trans->lowPrice();

		float y=(1.0f-offset)/vertInterval*MAX_HEIGHT;//downward y
		return y;
	}

	float DiagramScene::getXPos( Transaction1Day* trans, int index )
	{
		TransactionSingle* t=trans->getTransaction(index);
		int time=t->time;
		float offset=Utilities::subRestSeconds(time);
		float total=14400+10;//9:30-11:30 13:00-15:00 4*3600 14400 +10 means delay
		

		float x=offset/total*MAX_WIDTH;
		return x;
	}

	float DiagramScene::get2MinutesYPos( Transaction2Minutes* t )
	{
		
		float height=(t->balancePrice()-trans->lowPrice())/(trans->highPrice()-trans->lowPrice())*MAX_HEIGHT;//downward y
		float y=MAX_HEIGHT-height;
		assert(y>=0);
		return y;

	}

	float DiagramScene::get2MinutesXPos( Transaction2Minutes* t )
	{
		float index=t->index();

		float total=120;//9:30-11:30 13:00-15:00 4*3600 14400 +10 means delay


		float x=(index/total)*MAX_WIDTH;
		return x;

	}

	void DiagramScene::showInfo( DiagramNode* node )
	{
		if(!_infoBox){
			_infoBox=new NodeInfoBox();
			
		}
		

		QPointF pt=node->scenePos();

		_infoBox->setPos(pt);
		_infoBox->setNode(node);
		
		//_infoBox->show();
		this->addItem(_infoBox);
		
	}

	void DiagramScene::hideInfo()
	{
		//_infoBox->hide();
		this->removeItem(_infoBox);

	}

	void DiagramScene::clearNodes()
	{
		setSceneRect(0,0,MAX_WIDTH,MAX_HEIGHT);
		clear();//because clear all , the info box is clear too

		_transactions.clear();
		_nodes.clear();
		_edges.clear();

		_timelineItem=0;
		_timeIndicator=0;
		_infoBox=0;
		_transInfoItem=0;
		_priceIndicator=0;

	}

	void DiagramScene::addBottomTimeLine()
	{
		if(_timelineItem){
			return;
		}

		_timelineItem=new TimelineItem(_view);
		_timelineItem->setPos(0,MAX_HEIGHT-20);
		this->addItem(_timelineItem);
		
		
	}

	void DiagramScene::addBottomTimeLineTrans( float x, Transaction* t )
	{
		if(!_timelineItem){
			return;
		}
		_timelineItem->addTrans(x,t);
	}

	void DiagramScene::addTopTransInfoItem()
	{
		if(_transInfoItem){
			return;
		}

		_transInfoItem=new TransactionInfoItem(_view);
		_transInfoItem->setPos(0,0);
		this->addItem(_transInfoItem);
		
	}

	Transaction* DiagramScene::getNearestTrans( float x )
	{
		//get day from position
		int cnt=_transactions.size();
		if(cnt==0){
			return 0;
		}
			//float x=float(i)/((float)cnt-1.0f)*(float)MAX_WIDTH;
		//x=i/(cnt-1)*max=>i=x*(cnt-1)/max
		int index=x/(float)MAX_WIDTH*(cnt-1);
		if(index>cnt-1){
			index=cnt-1;
		}
		return _transactions[index];
		
	}

	void DiagramScene::addTimeIndicatorItem()
	{
		if(_timeIndicator){
			return;
		}

		_timeIndicator=new TimeIndicatorItem(_view);
		_timeIndicator->setPos(0,0);
		this->addItem(_timeIndicator);

	}

	void DiagramScene::OnMouseMove(int x,int y)
	{
		if(!_timeIndicator){
			return;
		}

		_timeIndicator->setPos(x,0);

	}

	void DiagramScene::addPriceIndicator()
	{
		if(_priceIndicator){
			return;
		}

		_priceIndicator=new PriceIndicatorItem(_view);
		_priceIndicator->setPos(0,0);

		switch(_type){
			case Graph_Type_Price:
				_priceIndicator->setHighestValue(_dayHighestHighPrice->highPrice());
				_priceIndicator->setLowestValue(_dayLowestLowPrice->lowPrice());
				break;
			case Graph_Type_Capital:
				_priceIndicator->setHighestValue(_highestCapital.capital());
				_priceIndicator->setLowestValue(_lowestCapital.capital());
				break;
			case Graph_Type_Unknown:
				break;
		}
		
		this->addItem(_priceIndicator);
		
	}

	void DiagramScene::calcTransaction1Day( QString symbol, QDate startDate, QDate endDate )
	{
		StockTransactions* trans= TransactionDatabase::getSingleton().getTransactions(symbol);
		trans->loadTreeData();

		int startDay=Utilities::getDaysFrom1900(startDate);
		int endDay=Utilities::getDaysFrom1900(endDate);

		bool init=false;//whether high low inited
		for(int i=startDay;i<=endDay;++i){

			Transaction1Day* dayTrans=trans->getDay(i);
			if(!dayTrans||!dayTrans->statisticed()){
				continue;
			}

			_transactions.push_back(dayTrans);
			if(!init){
				_dayHighestBalancePrice=dayTrans;
				_dayLowestBalancePrice=dayTrans;

				_dayHighestHighPrice=dayTrans;
				_dayLowestHighPrice=dayTrans;

				_dayHighestLowPrice=dayTrans;
				_dayLowestLowPrice=dayTrans;

				_dayHighestOpenPrice=dayTrans;
				_dayLowestOpenPrice=dayTrans;

				_dayHighestClosePrice=dayTrans;
				_dayLowestClosePrice=dayTrans;

				init=true;
			}else{
				if(_dayHighestBalancePrice->balancePrice()<dayTrans->balancePrice()){
					_dayHighestBalancePrice=dayTrans;
				}

				if(_dayLowestBalancePrice->balancePrice()>dayTrans->balancePrice()){
					_dayLowestBalancePrice=dayTrans;
				}

				if(_dayHighestHighPrice->highPrice()<dayTrans->highPrice()){
					_dayHighestHighPrice=dayTrans;
				}

				if(_dayLowestHighPrice->highPrice()>dayTrans->highPrice()){
					_dayLowestHighPrice=dayTrans;
				}


				if(_dayHighestLowPrice->lowPrice()<dayTrans->lowPrice()){
					_dayHighestLowPrice=dayTrans;
				}

				if(_dayLowestLowPrice->lowPrice()>dayTrans->lowPrice()){
					_dayLowestLowPrice=dayTrans;
				}


				if(_dayHighestOpenPrice->openPrice()<dayTrans->openPrice()){
					_dayHighestOpenPrice=dayTrans;
				}

				if(_dayLowestOpenPrice->openPrice()>dayTrans->openPrice()){
					_dayLowestOpenPrice=dayTrans;
				}


				if(_dayHighestClosePrice->closePrice()<dayTrans->closePrice()){
					_dayLowestClosePrice=dayTrans;
				}

				if(_dayLowestClosePrice->closePrice()>dayTrans->closePrice()){
					_dayLowestClosePrice=dayTrans;
				}


			}

		}
	}


	void DiagramScene::calcCapital1Day( QString symbol, QDate startDate, QDate endDate )
	{
		StockTransactions* trans= TransactionDatabase::getSingleton().getTransactions(symbol);
		trans->loadTreeData();

		int startDay=Utilities::getDaysFrom1900(startDate);
		int endDay=Utilities::getDaysFrom1900(endDate);

		bool init=false;//whether high low inited
		for(int i=startDay;i<=endDay;++i){

			Transaction1Day* dayTrans=trans->getDay(i);
			if(!dayTrans||!dayTrans->statisticed()){
				continue;
			}

			_transactions.push_back(dayTrans);
			if(!init){
				double capital=StockDatabase::getSingleton().getStockCapital(dayTrans->symbol(),dayTrans->day());
				_highestCapital=StockCaptialInfo(dayTrans->symbol(),dayTrans->day(),capital);
				_lowestCapital=StockCaptialInfo(dayTrans->symbol(),dayTrans->day(),capital);
				init=true;
			}else{
				double capital=StockDatabase::getSingleton().getStockCapital(dayTrans->symbol(),dayTrans->day());
				if(capital>_highestCapital.capital()){
					_highestCapital=StockCaptialInfo(dayTrans->symbol(),dayTrans->day(),capital);
				}else if(capital<_lowestCapital.capital()){
					_lowestCapital=StockCaptialInfo(dayTrans->symbol(),dayTrans->day(),capital);
				}

			}


		}
	}


	int DiagramScene::getHighestPriceYPos()
	{

		float y=Top_Height;
		return y;

	}

	int DiagramScene::getLowestPriceYPos()
	{
		float y=MAX_HEIGHT-Bottom_Height;
		return y;

	}

	void DiagramScene::loadDayPriceEx( QString symbol )
	{
		QDate startDate=TransactionDatabase::getSingleton().getStartDate(symbol);
		QDate endDate=TransactionDatabase::getSingleton().getEndDate(symbol);


		loadDayPriceEx(symbol,startDate,endDate);


	}

	void DiagramScene::loadDayPriceEx( QString symbol,QDate startDate,QDate endDate )
	{
		_type=Graph_Type_Price;
		clearNodes();

		calcTransaction1Day(symbol, startDate, endDate);


		addTopTransInfoItem();
		addTimeIndicatorItem();
		addBottomTimeLine();
		addPriceIndicator();

		int cnt=_transactions.size();
		for(int i=0;i<cnt;++i){
			float x=float(i)/((float)cnt-1.0f)*(float)MAX_WIDTH;


			float total=_dayHighestHighPrice->highPriceEx()-_dayLowestLowPrice->lowPriceEx();
			float offset=_transactions[i]->balancePriceEx()-_dayLowestLowPrice->lowPriceEx();
			

			float y=MAX_HEIGHT-offset/total*(float)(MAX_HEIGHT-Top_Height-Bottom_Height)-Bottom_Height;//downward y//20 is bottom

			DiagramNode *node=new DiagramNode(_view,_transactions[i]);
			this->addItem(node);


			Transaction* t=_transactions[i];
			addBottomTimeLineTrans(x,t);

			int cnt=_nodes.size();
			if(cnt>0){//have prev node
				DiagramEdge* e=new DiagramEdge(node, _nodes[cnt-1]);
				this->addItem(e);
				_edges.push_back(e);

			}

			node->setPos(x,y);
			_nodes.push_back(node);


		}





	}

}
